#ifndef IF_EXPRESSION_NODE_H
#define IF_EXPRESSION_NODE_H

#include "ExpressionNode.h"

template <typename T>
static Column computeIf(Column conditions, Column trueValues, Column falseValues);

template <>
Column computeIf<bool>(Column conditions, Column trueValues, Column falseValues) {
	SharedArray<bool> out(conditions.size());

	for (int i = 0; i < conditions.size(); ++i) {
		out[i] = (conditions.at<bool>(i) ? trueValues.at<bool>(i) : falseValues.at<bool>(i));
//		out[i] = (conditions.at<bool>(i) & trueValues.at<bool>(i)) | ((!conditions.at<bool>(i)) & falseValues.at<bool>(i));
	}

	return Column(out);
}

template <>
Column computeIf<int32_t>(Column conditions, Column trueValues, Column falseValues) {
	SharedArray<int32_t> out(conditions.size());

	for (int i = 0; i < conditions.size(); ++i) {
		out[i] = (conditions.at<bool>(i) ? trueValues.at<int32_t>(i) : falseValues.at<int32_t>(i));
//		out[i] = (conditions.at<bool>(i) * trueValues.at<int32_t>(i)) | ((!conditions.at<bool>(i)) * falseValues.at<int32_t>(i));
	}

	return Column(out);
}

template <>
Column computeIf<double>(Column conditions, Column trueValues, Column falseValues) {
	SharedArray<double> out(conditions.size());

	for (int i = 0; i < conditions.size(); ++i) {
		out[i] = (conditions.at<bool>(i) ? trueValues.at<double>(i) : falseValues.at<double>(i));
/*		assert((int) conditions.at<bool>(i) == 1 || (int) conditions.at<bool>(i) == 0);
		assert(sizeof(uint64_t) == sizeof(double));
		uint64_t* a = (uint64_t*) trueValues.data<double>() + i;
		uint64_t* b = (uint64_t*) falseValues.data<double>() + i;
		uint64_t res = (conditions.at<bool>(i) * (*a)) + ((!conditions.at<bool>(i)) * (*b));
		out[i] = *((double*) (&res));*/
	}

	return Column(out);
}

class IfExpressionNode: public ExpressionNode {
	public:
		IfExpressionNode(ExpressionNode* conditionChild, ExpressionNode* trueChild, ExpressionNode* falseChild):
				_conditionChild(conditionChild),
				_trueChild(trueChild),
				_falseChild(falseChild) {}

		~IfExpressionNode() {
			delete _conditionChild;
			delete _trueChild;
			delete _falseChild;
		}

		virtual Column pull(const SharedArray<Column>& data) {
			Column conditions = _conditionChild->pull(data);
			Column trueValues = _trueChild->pull(data);
			Column falseValues = _falseChild->pull(data);

			assert(conditions.type() == BOOL);
			assert(trueValues.type() == falseValues.type());
			assert(conditions.size() == trueValues.size());
			assert(conditions.size() == falseValues.size());

			switch (trueValues.type()) {
				case INT: return computeIf<int32_t>(conditions, trueValues, falseValues);
				case DOUBLE: return computeIf<double>(conditions, trueValues, falseValues);
				case BOOL: return computeIf<bool>(conditions, trueValues, falseValues);
				default: assert(false);
			}

			return Column();
		}

	private:
		ExpressionNode* _conditionChild;
		ExpressionNode* _trueChild;
		ExpressionNode* _falseChild;
};

#endif
